import html2canvas from 'html2canvas';
import jsPDF from '../../utils/thirdPackage/jspdf';

export default function (
  gap = 16, // 元素间隔(HTML)
  HTMLWidth = 1301, // HTML页面宽度(HTML)，对应的高度为920
  // 纸张横向打印
  pdfWidth = 841.89, // PDF纸张的宽度(PDF)
  pdfHeight = 595.28, // PDF纸张高度(PDF)
  pdfLeftRightGap = 20, // 在PDF中左右留的边距(PDF)
  pdfTopGap = 20, // 在pdf上面预留的高度
  pdfBottomGap = 26, // 在pdf下面预留的高度
) {
  this.gap = gap; // 元素间隔(HTML)
  this.HTMLWidth = HTMLWidth; // HTML页面宽度(HTML)，对应的高度为920

  // 纸张横向打印
  this.pdfWidth = pdfWidth; // PDF纸张的宽度(PDF)
  this.pdfHeight = pdfHeight; // PDF纸张高度(PDF)
  this.pdfLeftRightGap = pdfLeftRightGap; // 在PDF中左右留的边距(PDF)
  this.pdfTopGap = pdfTopGap; // 在pdf上面预留的高度
  this.pdfBottomGap = pdfBottomGap; // 在pdf下面预留的高度（后期添加页码）

  // 生成空白canvas元素
  this.blankImgData = function (height) {
    // 创建空白元素，可添加到PDF中的对象
    const blankCanvas = document.createElement('canvas');
    blankCanvas.width = this.pdfWidth;
    blankCanvas.height = height;
    blankCanvas.style.backgroundColor = 'white';
    var ctx = blankCanvas.getContext('2d');
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, this.pdfWidth, height);
    let blankImgData = blankCanvas.toDataURL('image/jpeg', 1);
    return blankImgData;
  };

  // 创建空白头元素，可添加到PDF中的对象（每页头部空白）
  this.headerBlankImgData = this.blankImgData(this.pdfTopGap);

  /** 生成table header canvas元素，因图片加载是异步操作，股该函数返回promise，外层调用可用await来改为同步
   *  img：含有表头的图片
   *  tableHeaderHeight: 表头高度（在HTML中）
   *  yPosi: 表头在img图片的纵向位置（在HTML中
   */
  this.tableHeaderImgData = function (img, tableHeaderHeight, yPosi) {
    let HTMLWidth = this.HTMLWidth;
    return new Promise((resolve) => {
      // 创建空白元素，可添加到PDF中的对象
      const headerCanvas = document.createElement('canvas');
      headerCanvas.width = HTMLWidth; // canvas宽度
      headerCanvas.height = tableHeaderHeight; // canvas高度
      headerCanvas.style.backgroundColor = 'white';
      let ctx = headerCanvas.getContext('2d');

      // 新建图片，数据源为html转canvas生成的图片数据
      let image = new Image();
      image.src = img;
      // 图片的加载是一个异步过程
      image.onload = function () {
        /** canvas截取指定位置和高度的一段图片
         *  必须将img数据转成image图片后才可以执行drawImage，否则会报错
         *  参数为： drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
         *     img：规定要使用的图像、画布或视频
         *     sx：可选。开始剪切的 x 坐标位置
         *     sy：可选。开始剪切的 y 坐标位置。
         *     swidth：可选。被剪切图像的宽度
         *     sheight：可选。被剪切图像的高度
         *     x：在画布上放置图像的 x 坐标位置
         *     y：在画布上放置图像的 y 坐标位置
         *     width：可选。要使用的图像的宽度。（伸展或缩小图像）
         *     height：可选。要使用的图像的高度。（伸展或缩小图像）
         */

        ctx.drawImage(
          image, // 规定要使用的图像、画布或视频
          0, // 开始剪切的 x 坐标位置
          yPosi * 2, // 开始剪切的 y 坐标位置，因在执行html2canvas时，设置的scale为2，故图片放大了两倍
          HTMLWidth * 2, // 被剪切图像的宽度，因在执行html2canvas时，设置的scale为2，故图片放大了两倍
          tableHeaderHeight * 2, // 被剪切图像的高度，因在执行html2canvas时，设置的scale为2，故图片放大了两倍
          0, // 在画布上放置图像的 x 坐标位置
          0, // 在画布上放置图像的 y 坐标位置
          HTMLWidth, // 要使用的图像的宽度。（伸展或缩小图像）
          tableHeaderHeight, // 要使用的图像的高度。（伸展或缩小图像）
        );
        let headerImgData = headerCanvas.toDataURL('image/jpeg', 1);
        resolve(headerImgData);
      };
    });
  };
  // 生成页码元素

  this.pageImgData = function (page) {
    // 创建页码元素，添加到每页末尾
    const pageCanvas = document.createElement('canvas');
    let width = this.HTMLWidth;
    let height = (this.HTMLWidth * this.pdfBottomGap) / (this.pdfWidth - this.pdfLeftRightGap * 2);
    pageCanvas.width = width;
    pageCanvas.height = height;
    var ctx = pageCanvas.getContext('2d');
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, width, height);

    // 画一条线
    ctx.moveTo(0, 0);
    ctx.lineTo(this.HTMLWidth, 0);
    ctx.strokeStyle = '#DDDDDD';
    ctx.stroke();

    // 写上页码
    ctx.fillStyle = '#333333';
    ctx.font = '14px sans-serif';
    ctx.fillText(page > 9 ? page : `0${page}`, this.HTMLWidth - 25, 20);

    let pageImgData = pageCanvas.toDataURL('image/jpeg', 1);
    return pageImgData;
  };

  /**
   * @param id  父节点id
   * @param drawingItemName  导出元素name
   * @param name  导出文件名称
   * @param callback
   */
  this.savePDF = function (id, drawingItemName, name, callback) {
    setTimeout(() => {
      html2canvas(document.getElementById(id), {
        scale: 2,
        timeout: 1000,
        // useCORS: true,
      }).then(async (canvas) => {
        // 可添加到PDF中的对象 （当前需要转为PDF的全部动态元素）
        const imgData = canvas.toDataURL('image/jpeg', 1);

        // 横向打印，A4纸张大小
        const pdfObj = new jsPDF('landscape', 'pt', 'a4'); // PDF对象
        const pdfInnerWidth = this.pdfWidth - this.pdfLeftRightGap * 2; // PDF内部去掉边距内部实际可使用的宽度
        const pdfInnerHeight = this.pdfHeight - this.pdfTopGap - this.pdfBottomGap; // PDF内部去掉编辑内部实际可使用的高度
        const gapInPDFH = (this.gap * pdfInnerWidth) / this.HTMLWidth; // 元素之间的空隙在PDF中的高度
        const currentHTMLWidth = canvas.width; // 当前需要转换为PDF的元素的宽度 （HTML宽度）
        const currentHTMLHeight = canvas.height; // 当前需要转换的PDF的元素的高度（HTML的高度）
        const currentPdfHeight = (pdfInnerWidth * currentHTMLHeight) / currentHTMLWidth; // 当前需要转换为PDF的元素在PDF中高度
        const elements = document.getElementsByName(drawingItemName); // 需要导出的元素
        const boardYPosi = document.getElementById(id).offsetTop; // 画板距离页面最上方的距离(用于计算表头在html中的位置)

        let nextIndex = 0; // 下一页PDF起始的元素序号
        let indexing = -1; // 标记正在执行第几个元素
        let htmlInPDFHeight = 0; // 已经在PDF上的元素的高度
        let currentPage = 1; // 记录当前正在处理pdf的第几页
        let finish = false; // 记录是否完成分析，可以执行导出了

        // 以下变量与表格换页相关
        let pdfTableHeader = 0; // 记录需要换页的表头的高度（在pdf中，根据每一次循环会有所变化）
        let headerHight = 0; // 记录需要换页的表头的高度（在html当中）
        let trIndex = 0; // 记录需要换页的表格已经处理过多少行了（根据每一次循环会有所变化）
        let tableRow = undefined; // 记录需要换页的表格行信息（根据每一次循环会有所变化）
        let continueTable = false; // 标记下轮循环是不是以未完成的表格开始
        let headerPosiInImg = 0; // 记录需要换页的表头在图片当中的位置，便于canvas截取表头，换页时贴在头部(在html当中)
        let needHeader = false; // 标记需要换页的表格后面还有别的内容，在将最后一页表格和后面的内容汇整成一页pdf后，同样需要在表格上添加表头
        let preTableHeadInfo = null; // 当上一个分页的表格的最后一页还未添加到pdf，同页又出现了需要换页的表格，那么以上换页的字段会记录最新这一次的表头信息，这个时候，该对象中存储上一个分页信息，当页处理完后，继续设置为null
        let tableHeaderImage = null; // 生成的需要换页的表头图片

        while (finish == false) {
          // 若该变量中记录这前一个分页的信息，那个分页内容已经打印到PDF，新一轮循环不再需要，清除
          preTableHeadInfo = null;

          // 刚开始时，累计到pdf的元素的高度是0，如果不是第一页，则添加一页PDF
          if (htmlInPDFHeight != 0) {
            pdfObj.addPage();
            currentPage++;
          }

          let height = 0; // 搜集当前一页元素的累计高度（PDF的高度，若有表格换页，该变量中包含额外添加的表头的高度，但实际上额外添加的表头高度不属于已经累计到pdf上的元素的高度）
          let realPdfHeight = 0; // 搜集当前一页元素的累计高度（PDF的高度，不包含表格换页后，接下来页中虚拟表头的高度）

          // 若上一页，有需要换页的表格，则本页，先把表头加上
          if (continueTable == true) {
            height += pdfTableHeader; // 累计虚拟表头的高度

            let pdfRowsHeight = 0; // 当前累计行高
            // 从上一次未处理的第一行开始遍历
            for (let j = trIndex; j < tableRow.length; j++) {
              let pdfOneRow = (tableRow[j].clientHeight * pdfInnerWidth) / this.HTMLWidth;
              // 当添加了一行超过pdf高度了，则记录下一页要开始的行号
              if (pdfRowsHeight + pdfOneRow > pdfInnerHeight - pdfTableHeader) {
                break; // 此处是中断当前for循环
              }
              pdfRowsHeight += pdfOneRow; // 当前累计行高加上当前行的行高
              height += pdfOneRow; // 累计当前页已将添加到pdf上的高度
              realPdfHeight += pdfOneRow;
              trIndex++;
            }

            // 若当前页还是不能满足表格的高度，则继续
            if (trIndex < tableRow.length) {
              continueTable = true; // 标记下一页开始，就处理未完成的表格
            } else {
              // 若当前页已经处理完了上页遗留的表格
              continueTable = false; // 标记下轮循环不是以未完成的表格开始
              trIndex = 0;

              if (indexing != elements.length - 1) {
                // 若本次循环中，遗留的表格不是要转PDF的最后一个元素，则标记后续处理中，需为表格添加表头
                needHeader = true;
              } else {
                // 否则清空表头图片
                tableHeaderImage = null;
              }
            }
          }

          let i;
          if (continueTable == false) {
            // 针对每一个元素，逐个遍历，从上一页未处理的第一个元素开始遍历
            for (i = nextIndex; i < elements.length; i++) {
              indexing++; // 标记当前正在执行第几个元素

              // 若不是当前页的第一个元素，则累积高度添加间隔
              // 若刚处理完遗留的表格，继续执行下一个元素，中间不存在换页，那么也需要在表格的最后与当前元素之间添加间隙
              if (i != nextIndex || needHeader == true) {
                height += gapInPDFH;
                realPdfHeight += gapInPDFH;
              }

              // 计算当前元素在pdf中占的高度
              let pdfCurrentItemHeight = (elements[i].clientHeight * pdfInnerWidth) / this.HTMLWidth;

              // 若累计当前元素后，大于了内容区的高度（证明当前元素不能完全放入该页）
              if (height + pdfCurrentItemHeight > pdfInnerHeight) {
                // 如果是表格，则处理表格的分页问题
                if (elements[i].getAttribute('istable') == 'true') {
                  //表头
                  let tableHeader = elements[i].getElementsByTagName('thead')[0];
                  //表格body
                  let tableBody = elements[i].getElementsByTagName('tbody')[0];
                  if (tableHeader && tableBody) {
                    // 当上一个表格元素的换页行为还没执行完，当页又来了一个需要换页的表格时，把原表头信息记录到preTableHeadInfo
                    if (needHeader) {
                      preTableHeadInfo = {
                        pdfTableHeader: pdfTableHeader, // 记录需要换页的表头的高度
                        trIndex: trIndex, // 记录需要换页的表格已经处理过多少行了
                        tableRow: tableRow, // 记录需要换页的表格行信息
                        headerHight: headerHight, // 记录需要换页的表头的高度（在html当中）
                        headerPosiInImg: headerPosiInImg, // 记录需要换页的表头在图片当中的位置
                        tableHeaderImage: tableHeaderImage, // 记录需要换页的表头图片
                      };
                      // 初始化数据，当前页中，另一个需要换页的表格，应该从第0行开始
                      trIndex = 0;
                      // 初始化表头图片
                      tableHeaderImage = null;
                    }

                    headerHight = tableHeader.clientHeight; // 记录需要换页的表头的高度（在html当中）
                    headerPosiInImg = elements[i].offsetTop - boardYPosi; // 记录需要换页的表头在图片当中的位置
                    // 计算表头高度
                    pdfTableHeader = (tableHeader.clientHeight * pdfInnerWidth) / this.HTMLWidth;

                    // 逐行计算body区域与剩余区域高度的比较，获取当前页可以放多少行

                    tableRow = tableBody.getElementsByTagName('tr');

                    // 表头的高度+第一行的高度如果小于剩余区域，那就执行表格分页
                    // 默认表格至少一行数据，如果一行都没有，则不展示
                    if (pdfTableHeader + (tableRow[0].clientHeight * pdfInnerWidth) / this.HTMLWidth + height <= pdfInnerHeight) {
                      height += pdfTableHeader;
                      realPdfHeight += pdfTableHeader; // 此时是真实的表头，不是虚拟表头

                      // 计算剩余区域高度
                      let pdfLastHeight = pdfInnerHeight - height;

                      let pdfRowsHeight = 0; // 当前累计行高
                      for (let j = trIndex; j < tableRow.length; j++) {
                        let pdfOneRow = (tableRow[j].clientHeight * pdfInnerWidth) / this.HTMLWidth;
                        // 当添加了一行超过pdf高度了，则记录下一页要开始的行号
                        if (pdfRowsHeight + pdfOneRow > pdfLastHeight) {
                          trIndex = j;
                          break; // 此处是中断当前for循环
                        }
                        pdfRowsHeight += pdfOneRow; // 当前累积行高加上当前行的行高
                        height += pdfOneRow;
                        realPdfHeight += pdfOneRow;
                      }

                      // 标记下一页开始，就处理未完成的表格
                      continueTable = true;
                      nextIndex = i + 1; // 记录表格处理完后，下一个要处理的元素的序号
                      break; // 此处是中断当前for循环
                    } else {
                      nextIndex = i; // 记录下一轮开始的元素
                      indexing--; // 若当前遍历的元素高度超过了空白区，则本页不再处理该元素
                      break;
                    }
                  } else {
                    // istable=true时，但里面却不是表格时（如数据异常），则跳出本次for循环，执行下一轮while（另起一页）
                    nextIndex = i; // 记录下一轮开始的元素
                    indexing--; // 若当前遍历的元素高度超过了空白区，则本页不再处理该元素
                    break;
                  }
                } else {
                  // 若不是表格，则跳出本次for循环，执行下一轮while（另起一页）
                  nextIndex = i; // 记录下一轮开始的元素
                  indexing--; // 若当前遍历的元素高度超过了空白区，则本页不再处理该元素
                  break;
                }
              } else {
                // 若空白区域足够放当前元素
                height += pdfCurrentItemHeight;
                realPdfHeight += pdfCurrentItemHeight;
              }
            }
          }

          /**------ 在换页之前（一次while结尾）添加一页PDF---------- */

          // 叠加元素(height != realPdfHeight时，意味上叠加了表头,PDF的位置应该向下移动一个表头的位置)
          if (height != realPdfHeight) {
            if (needHeader && preTableHeadInfo != null) {
              // 用上一轮未删pdf的表头
              pdfObj.addImage(imgData, 'JPEG', this.pdfLeftRightGap, this.pdfTopGap - htmlInPDFHeight + preTableHeadInfo.pdfTableHeader, pdfInnerWidth, currentPdfHeight);
            } else {
              pdfObj.addImage(imgData, 'JPEG', this.pdfLeftRightGap, this.pdfTopGap - htmlInPDFHeight + pdfTableHeader, pdfInnerWidth, currentPdfHeight);
            }
          } else {
            pdfObj.addImage(imgData, 'JPEG', this.pdfLeftRightGap, this.pdfTopGap - htmlInPDFHeight, pdfInnerWidth, currentPdfHeight);
          }

          // 叠加表头(height != realPdfHeight时，意味着叠加了表头)
          if (height != realPdfHeight) {
            if (needHeader && preTableHeadInfo != null) {
              // 用上一轮未删pdf的表头
              if (preTableHeadInfo.tableHeaderImage == null || preTableHeadInfo.tableHeaderImage == undefined) {
                preTableHeadInfo.tableHeaderImage = await this.tableHeaderImgData(imgData, preTableHeadInfo.headerHight, preTableHeadInfo.headerPosiInImg);
              }

              needHeader = false;
              pdfObj.addImage(preTableHeadInfo.tableHeaderImage, 'JPEG', this.pdfLeftRightGap, this.pdfTopGap, pdfInnerWidth, preTableHeadInfo.pdfTableHeader);
              preTableHeadInfo = null;
            } else {
              if (tableHeaderImage == null) {
                tableHeaderImage = await this.tableHeaderImgData(imgData, headerHight, headerPosiInImg);
              }
              needHeader = false;
              pdfObj.addImage(tableHeaderImage, 'JPEG', this.pdfLeftRightGap, this.pdfTopGap, pdfInnerWidth, pdfTableHeader);
            }
          }

          // 叠加头部空白
          pdfObj.addImage(this.headerBlankImgData, 'JPEG', this.pdfLeftRightGap, 0, pdfInnerWidth, this.pdfTopGap);

          // 叠加尾部空白
          const endBlankImgData = this.blankImgData(pdfInnerHeight - height + this.pdfBottomGap);
          pdfObj.addImage(endBlankImgData, 'JPEG', this.pdfLeftRightGap, this.pdfTopGap + height, pdfInnerWidth, pdfInnerHeight - height + this.pdfBottomGap);

          // 叠加页码
          const pageImgData = this.pageImgData(currentPage);
          pdfObj.addImage(pageImgData, 'JPEG', this.pdfLeftRightGap, this.pdfHeight - this.pdfBottomGap, pdfInnerWidth, this.pdfBottomGap);

          // 记录已经复制到pdf上的元素的pdf高度
          htmlInPDFHeight += realPdfHeight;

          // 最后一个元素，且没有未处理完的表格，则退出循环
          if (i == elements.length && continueTable == false) {
            finish = true;
          }
        }

        pdfObj.save(`${name}.pdf`);
        callback('success');
      });
    }, 1000);
  };
}
